// /////////////////////////////////////////////////////////////////////////////
//
// Name:            SceneComponent.h
// Author:          Michael Bartsch (ZCCdark203)
//
// Desc :           Declaration of the SceneComponent class.
//
// License:         Copyright (C) 2009 Michael Bartsch and Contributors
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the zlib/libpng License.
//                  See main.cpp for conditions of distribution and use.
//
// /////////////////////////////////////////////////////////////////////////////

#ifndef __SCENECOMPONENT_H__
#define __SCENECOMPONENT_H__

// Include files
#include "../../dependencies.h"
#include "../../core/EntityComponent.h"


// SceneComponent class
//! Component wrapper of Irrlicht's ISceneNode and base class of various scene components.
//!
//! <b>XML:</b>
//! \code
//! <SceneComponent>
//!    <!-- Properties -->
//! </SceneComponent>
//! \endcode
class SceneComponent : public EntityComponent
{
public:

    // Initialisation and deinitialisation
    //! Constructor (default)
    SceneComponent(Entity *parent);
    //! Deconstructor
    ~SceneComponent();

    // Methods
    //! Returns a direct pointer to the ISceneNode.
    //! @note Not available in AngelScript.
    ISceneNode* getSceneNode();
    //! Returns a direct pointer to the meta selector of this component.
    //! @note Not available in AngelScript.
    IMetaTriangleSelector* getMetaSelector() const;
    //! Returns a direct pointer to the triangle selector of this component.
    //! @note Not available in AngelScript.
    ITriangleSelector* getTriangleSelector() const;

    //! Adds an scene node animator to the component.
    //! @param animator      Pointer to the animator which should be added.
    //! @note Don't forget to call SceneComponent::setCanAffectParent when you use an animator.
    void addAnimator(ISceneNodeAnimator *animator);

    //! Adds a collision response animator to the component.
    //!
    //! <b>XML:</b>
    //! \code
    //! <CollisionResponseAnimator>
    //!    <ellipsoidRadius x="" y="" z="" />
    //!    <gravityPerSecond x="" y="" z="" />
    //!    <ellipsoidTranslation x="" y="" z="" />
    //!    <slidingValue value="" />
    //! </CollisionResponseAnimator>
    //! \endcode
    //!
    //! @param ellipsoidRadius         Radius of the ellipsoid with which collision detection and
    //!                                response is done.
    //! @param gravityPerSecond        Sets the gravity of the environment, as an acceleration in
    //!                                units per second per second.
    //! @param ellipsoidTranslation    By default, the ellipsoid for collision detection is created
    //!                                around the center of the component, which means that the
    //!                                ellipsoid surrounds it completely. If this is not what you
    //!                                want, you may specify a translation for the ellipsoid.
    //! @param slidingValue            Sets the sliding value.
    //! @note Don't forget to call SceneComponent::setCanAffectParent when you use an animator.
    void addCollisionResponseAnimator(const vector3df &ellipsoidRadius = vector3df(30, 60, 30),
                                      const vector3df &gravityPerSecond = vector3df(0, -10, 0),
                                      const vector3df &ellipsoidTranslation = vector3df(0, 0, 0),
                                      f32 slidingValue = 0.0005f);

    //! Adds a fly circle animator to the component.
    //!
    //! <b>XML:</b>
    //! \code
    //! <FlyCircleAnimator>
    //!    <center x="" y="" z="" />
    //!    <radius value="" />
    //!    <speed value="" />
    //!    <direction x="" y="" z="" />
    //! </FlyCircleAnimator>
    //! \endcode
    //!
    //! @param center        Center of the circle.
    //! @param radius        Radius of the circle.
    //! @param speed         Specifies the speed of the flight.
    //! @param direction     Specifies the upvector used for alignment of the mesh.
    //! @note Don't forget to call SceneComponent::setCanAffectParent when you use an animator.
    void addFlyCircleAnimator(const vector3df &center = vector3df(0, 0, 0), f32 radius = 100,
                              f32 speed = 0.001f, const vector3df &direction = vector3df(0, 0, 0));

    //! Adds a fly straight animator to the component.
    //!
    //! <b>XML:</b>
    //! \code
    //! <FlyStraightAnimator>
    //!    <startPoint x="" y="" z="" />
    //!    <endPoint x="" y="" z="" />
    //!    <timeForWay value="" />
    //!    <loop value="" />
    //! </FlyStraightAnimator>
    //! \endcode
    //!
    //! @param startPoint    Start point of the line.
    //! @param endPoint      End point of the line.
    //! @param timeForWay    Time in milliseconds how long the component should need to move from
    //!                      the start point to the end point.
    //! @param loop          If set to false, the component stops when the end point is reached. If
    //!                      loop is true, the component begins again at the start.
    //! @note Don't forget to call SceneComponent::setCanAffectParent when you use an animator.
    void addFlyStraightAnimator(const vector3df &startPoint, const vector3df &endPoint,
                                u32 timeForWay, bool loop = false);

    //! Searches for a SceneComponent within the given entity and if found adds the triangle
    //! selector of that component to the meta selector of this component.
    //! @param entity        Pointer to the entity.
    void addToMetaSelector(Entity *entity);
    //! Adds the triangle selector of the given component to the meta selector of this
    //! component.
    //! @param component     Pointer to the component.
    void addToMetaSelector(SceneComponent *component);
    //! Adds the given triangle selector to the meta selector of this component.
    //! @param selector      Pointer to the triangle selector that should be added.
    //! @note Not available in AngelScript.
    void addToMetaSelector(ITriangleSelector *selector);

    //! Gets the abolute position of the component.
    vector3df getAbsolutePosition() const;
    //! Gets the automatic culling state.
    E_CULLING_TYPE getAutomaticCulling() const;
    //! Get the axis aligned, not transformed bounding box of this component.
    virtual const aabbox3df& getBoundingBox() const;
    //! Get amount of materials used by this component.
    u32 getMaterialCount() const;
    //! Returns the relative transformation of the component.
    matrix4 getRelativeTransformation() const;
    //! Gets the relative position of the component.
    const vector3df getPosition() const;
    //! Gets the rotation of the component.
    const vector3df& getRotation() const;
    //! Gets the scale of the component.
    const vector3df& getScale() const;

    //! Removes all animators from this component.
    void removeAnimators();

    //! Sets the absolute position of the component.
    //!
    //! <b>XML:</b>
    //! \code
    //! <absolutePosition value="" />
    //! \endcode
    //!
    //! @param position      New absolute position of the component.
    void setAbsolutePosition(const vector3df &position);
    //! Enables or disables automatic culling based on the bounding box.
    //!
    //! <b>XML:</b>
    //! \code
    //! <automaticCulling value="" />
    //! \endcode
    //!
    //! @param state         The culling state to be used.
    void setAutomaticCulling(E_CULLING_TYPE state);
    //! Sets whether the component can affect the parent entity.
    //! A direct effect of enabling this option is that the position of the parent entity and
    //! this component will always be synchronised.
    //!
    //! <b>XML:</b>
    //! \code
    //! <canAffectParent value="" />
    //! \endcode
    //!
    //! @param value         Value to enable or disable this function.
    //! @note It's recommended to enable this option when you're using animators.
    void setCanAffectParent(bool value);
    //! Sets if debug data like bounding boxes should be drawn.
    //! @param state         The debug data visibility state to be used.
    void setDebugDataVisible(s32 state);
    //! Sets a material flag to a new value.
    //!
    //! <b>XML:</b>
    //! \code
    //! <materialFlag flag="" value="" />
    //! \endcode
    //!
    //! @param flag          Which flag of all materials to be set.
    //! @param value         New value of that flag.
    void setMaterialFlag(E_MATERIAL_FLAG flag, bool value);
    //! Loads and sets the texture of the specified layer in all materials of this component to
    //! the new texture.
    //!
    //! <b>XML:</b>
    //! \code
    //! <materialTexture layer="" fileName="" />
    //! \endcode
    //!
    //! @param layer         Layer of texture to be set.
    //! @param fileName      Filename of the texture to load.
    void setMaterialTexture(u32 layer, const std::string &fileName);
    //! Sets the texture of the specified layer in all materials of this component to the new
    //! texture.
    //! @param layer         Layer of texture to be set.
    //! @param texture       New texture to be used.
    //! @note Not available in AngelScript.
    void setMaterialTexture(u32 layer, ITexture *texture);
    //! Sets the material type of all materials in this component to a new material type.
    //!
    //! <b>XML:</b>
    //! \code
    //! <materialType value="" />
    //! \endcode
    //!
    //! @param type          New type of material to be set.
    void setMaterialType(E_MATERIAL_TYPE type);
    //! Sets the relative position of the component.
    //!
    //! <b>XML:</b>
    //! \code
    //! <position x="" y="" z="" />
    //! \endcode
    //!
    //! @param position      New relative postition of the component.
    void setPosition(const vector3df &position);
    //! Sets the rotation of the component.
    //!
    //! <b>XML:</b>
    //! \code
    //! <rotation x="" y="" z="" />
    //! \endcode
    //!
    //! @param rotation      New rotation of the component in degrees.
    virtual void setRotation(const vector3df &rotation);
    //! Sets the relative scale of the component.
    //!
    //! <b>XML:</b>
    //! \code
    //! <scale x="" y="" z="" />
    //! \endcode
    //!
    //! @param scale         New scale of the node
    void setScale(const vector3df &scale);
    //! Sets if the component should be visible or not.
    //!
    //! <b>XML:</b>
    //! \code
    //! <visible value="" />
    //! \endcode
    //!
    //! @param value         If the node shall be visible.
    void setVisible(bool value);

    // Events
    //! Updates the position of the component after its parent has been updated.
    //! @note For internal use only!
    void onPositionChange(void *p);
    //! Updates the rotation of the component after its parent has been updated.
    //! @note For internal use only!
    void onRotationChange(void *p);
    //! Updates the component if the parent is updated.
    //! @note For internal use only!
    void onUpdate(void *p);
    //! Pauses the component if the parent is paused.
    //! @note For internal use only!
    void onPause(void *p);
    //! Unpauses the component if the parent is paused.
    //! @note For internal use only!
    void onUnPause(void *p);
    //! Responds to changes of a texture attached to this component.
    //! @note For internal use only!
    void onTexture(void *p);

    // Serialization
    //! Serializes this object.
    //! @param attributes
    virtual bool onSerialization(SerializedAttributes *attributes);
    //! Deserializes this object.
    //! @param attributes
    virtual bool onDeserialization(SerializedAttributes *attributes);

protected:

    // Initialisation
    //! Constructor for derived classes.
    SceneComponent(Entity *parent, bool isDerived);

    // Protected Members
    ISceneNode *mSceneNode;
    IMetaTriangleSelector *mMetaSelector;
    ITriangleSelector *mTriSelector;

private:

    // Private methods.
    //! Common initialisation method.
    void init();

    // Private members
    vector3df mLastPos;
    vector3df mLastRot;
    bool mCanAffectParent;

    bool mEvokedParentChange;
    bool mWasVisible;

    std::map<u32, std::string> mTextureFileNames;
};

#endif
